package com.example.sevicedemo

import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import java.util.concurrent.TimeUnit

/**
 * @author ypk
 * 创建日期：2021/11/25  9:59
 * 描述：
 *
 * JobService 是Android6.0（API 23）开始新增的功能。原因也是google对后台任务的管控越来越严格。
 *
 * JobService 用于执行一些需要满足特定条件但不紧急的后台任务，
 * 利用 JobScheduler 来执行这些特殊的后台任务来减少电量的消耗。
 *
 * 研究了几种方法后，觉得还是JobSchedule相对效果还好点，
 * 主要原因是WorkManager的定时任务最短也需要15分钟，
 * 虽然JobSchedule在Android7.0后也这样的，但是可以通过别的办法实现
 *
 * 参考文章：https://blog.csdn.net/qq_38350635/article/details/106390343
 *
 * 推荐方案：jobservice的生命周期方法中再次schedule下jobservice，来实现定时任务,这样就能不受15分钟的限制了
 *
 * 注意清单文件要加： android:permission="android.permission.BIND_JOB_SERVICE"
 *
 */
public class MyJobService : JobService() {

    override fun onCreate() {
        println("MyJobService.onCreate")
        super.onCreate()
    }


    /**
     * @param jobParameters 系统创建回调给我们的，不用自己创建，用于获得一些job的参数。例如job id
     * @return true，你的job为活跃状态。表明你的工作需要继续执行。一般为false。
     * 1、你可以主动调用jobFinished 去告诉系统job工作已经完成。结束当前job。
     * 2、job需要的约束条件不在满足时也会结束当前job。例如用户使用 JobInfo.Builder为job添加了setRequiresCharging(boolean)电量约束。
     * 当用户吧设备电源关闭时，系统会立刻停止改job，该job的onStopJob方法会被回调。
     * <p>
     * 3、只要你的job正在执行时，系统就会持有你app的唤醒锁。（此方法调用之前，系统就会获得唤醒锁）在您主动调用obFinished或者系统调用
     * onStopJob后这把锁才被释放。
     * 4、返回false代表您写在此方法体中的工作已经完成，系统将释放该job的锁。系统不会去调用onStopJob
     * @function job执行时调用此方法。这个方法运行在app的主线程中。你要重写这个方法，做一些自己的逻辑工作。
     */
    // 触发build的开启机制时走此方法.返回值一般为false.走完后系统不会调用onStopJob.
    override fun onStartJob(params: JobParameters?): Boolean {
        println("MyJobService.onStartJob")

        // 推荐方案：jobservice的生命周期方法中再次schedule下jobservice，来实现定时任务,这样就能不受15分钟的限制了
        schedulePushWork(this);

        return false
    }

    /**
     * 1、当系统确定停止job时会调用此方法，如果不满足build设置的相关要求时会触发此方法。
     * 例如：你设置了setRequiredNetworkType（JobInfo.NETWORK_TYPE_ANY），执行任务期间你切换了wifi。
     * 2、这个方法回调时，系统将释放app的唤醒锁。
     *
     * @param jobParameters 系统创建回调给我们的，不用自己创建，用于获得一些job的参数。例如job id
     * @return true 向JobManager 表明你要基于创建工作时的重试条件重新 schedule 这个job。false表明
     * 彻底结束了这个job。
     * 3、无论返回值如何都表明当前的job执行完毕啦。
     */
    // 当系统确定停止job时会调用此方法，如果不满足build设置的相关要求时会触发此方法.
    override fun onStopJob(params: JobParameters?): Boolean {
        println("MyJobService.onStopJob")


        return false
    }

    companion object {
        val jobId: Int = 10;
        fun schedulePushWork(context: Context) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                val toMillis = TimeUnit.SECONDS.toMillis(3)

                val jobScheduler = context.getSystemService(JobScheduler::class.java)
                val componentName = ComponentName(context, MyJobService::class.java)

                val builder = JobInfo.Builder(jobId, componentName)

                //网络要求（例子JobInfo.NETWORK_TYPE_ANY表名需要有网）
                builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                //默认值为false。true表示此job需要在“充电”状态下工作。这里的充电可以理解为充电玩手机时，电量是增的。
                // （假如你使用usb插在电脑上边充电边看视屏，电量可能冲着还减着）
                //builder.setRequiresCharging(false)

                //设置设备在空闲的时候执行job，此条件一般很难达到。
                //builder.setRequiresDeviceIdle(false)

                //运行此job需要设备的可用存储空间不能太低。
                //builder.setRequiresStorageNotLow(true);

                // TODO: 2021/11/25 不能同setPeriodic一起使用，会引起报错
                // 设置任务运行最少延迟时间
                builder.setMinimumLatency(toMillis)
                //设置deadline，若到期还没有达到规定的条件则会开始执行
                builder.setOverrideDeadline(toMillis + TimeUnit.SECONDS.toMillis(10))

                //val bundle = PersistableBundle()
                //bundle.putInt("type", 1)
                //builder.setExtras(bundle)

                //设置间隔时间，不断的触发任务的启动,android 7后最少时间是15分钟，否则不生效，所以不用了
                //推荐方案：jobservice的生命周期方法中再次schedule下jobservice，来实现定时任务,这样就能不受15分钟的限制了
                //builder.setPeriodic(15 * 60 * 1000L);


                val ret = jobScheduler.schedule(builder.build())
                Log.d("MyJobService", "schedulePushWork... ret=$ret")
            }
        }


    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        println("MyJobService.onStartCommand")
        //此方法不在回调了
        return super.onStartCommand(intent, flags, startId)
    }
}